﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NAudio.Midi;
using NAudio.Wave;

namespace MidiPlayer
{
    class AudioPlayer
    {
        IWavePlayer waveOut;
        WaveChannel32 mainOutputStream;
        private MidiIn midiIn;

        public MidiPlayerSettings Settings;

        
        public bool IsPlaying()
        {
            if (waveOut != null)
            {
                return waveOut.PlaybackState == PlaybackState.Playing;
            }
            return false;
        }

        public bool IsMidiPlaying()
        {
            return midiIn != null;
        }


        public void StartPlayFile()
        {
            CreateWaveOut();

            waveOut.Play();
        }

        public void StartMidi()
        {
            CreateWaveOut();
            if (midiIn == null)
            {
                var Model = new MidiSettingsViewModel(Settings.MidiSettings);
                Model.Load();
                midiIn = new MidiIn(Model.FindDevice(Model.Settings.MidiInName, true));
                midiIn.MessageReceived += new EventHandler<MidiInMessageEventArgs>(midiIn_MessageReceived);
                midiIn.ErrorReceived += new EventHandler<MidiInMessageEventArgs>(midiIn_ErrorReceived);

                midiIn.Start();
            }

        }

        public void StopMidi()
        {
            if (midiIn != null)
            {
                midiIn.Stop();
                midiIn.Dispose();
                midiIn = null;
            }

        }

        void midiIn_MessageReceived(object sender, MidiInMessageEventArgs e)
        {
            if (Settings.MidiSettings.MidiMode == MidiSettingsDto.OutputMode.PlayFile)
            {
                if (e.MidiEvent.CommandCode == MidiCommandCode.NoteOn)
                {
                    var note = (NoteEvent)(e.MidiEvent);
                    if(note.NoteNumber == Settings.MidiSettings.MidiPlayNote)
                    {
                        CreateWaveOut();
                        waveOut.Play();
                    }
                } else if(e.MidiEvent.CommandCode == MidiCommandCode.NoteOff)
                {
                    var note = (NoteEvent)(e.MidiEvent);
                    if(Settings.MidiSettings.StopOnNoteRelease &&
                        note.NoteNumber == Settings.MidiSettings.MidiPlayNote)
                    {
                        waveOut.Stop();
                    }
                }

            }
         

        }

        void midiIn_ErrorReceived(object sender, MidiInMessageEventArgs e)
        {
            
        }



        public void StopPlayFile()
        {
            if (waveOut != null)
            {
                waveOut.Stop();
            }

        }


        private void CreateWaveOut()
        {
            Close();
            int latency = Settings.PlaySettings.Latency;

            if (Settings.PlaySettings.Output == PlaybackSettingsDto.Outputs.Asio )
            {
                waveOut = new AsioOut(Settings.PlaySettings.AsioDriverName);
            }
            else
            {
                waveOut = new DirectSoundOut(latency);
            }

            mainOutputStream = CreateInputStream(Settings.PlaybackFile);

            try
            {
                waveOut.Init(mainOutputStream);
            }
            catch (Exception initException)
            {
                MessageBox.Show(String.Format("{0}", initException.Message), "Error Initializing Output");
                return;
            }

        }


        public void Close()
        {
            
            if (waveOut != null)
            {
                waveOut.Stop();
            }
            if (mainOutputStream != null)
            {
                // this one really closes the file and ACM conversion
                mainOutputStream.Close();
                mainOutputStream = null;
            }
            if (waveOut != null)
            {
                waveOut.Dispose();
                waveOut = null;
            }
        }

        private WaveChannel32 CreateInputStream(string fileName)
        {
            WaveChannel32 inputStream;
            if (fileName.EndsWith(".wav"))
            {
                WaveStream readerStream = new WaveFileReader(fileName);
                if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
                {
                    readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
                    readerStream = new BlockAlignReductionStream(readerStream);
                }
                if (readerStream.WaveFormat.BitsPerSample != 16)
                {
                    var format = new WaveFormat(readerStream.WaveFormat.SampleRate,
                        16, readerStream.WaveFormat.Channels);
                    readerStream = new WaveFormatConversionStream(format, readerStream);
                }
                inputStream = new WaveChannel32(readerStream);
            }
            else if (fileName.EndsWith(".mp3"))
            {
                WaveStream mp3Reader = new Mp3FileReader(fileName);
                WaveStream pcmStream = WaveFormatConversionStream.CreatePcmStream(mp3Reader);
                WaveStream blockAlignedStream = new BlockAlignReductionStream(pcmStream);
                inputStream = new WaveChannel32(blockAlignedStream);
            }
            else
            {
                throw new InvalidOperationException("Unsupported extension");
            }

            return inputStream;
        }
    }
}
